﻿using AiBLSmartEdu.Module.SmartStockSense.API.Configurations;
using AiBLSmartEdu.Module.SmartStockSense.API.DTOs;
using AiBLSmartEdu.Module.SmartStockSense.API.Hubs;
using AiBLSmartEdu.Module.SmartStockSense.API.Services;
using Domain.Entities.Stocks;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace AiBLSmartEdu.Module.SmartStockSense.API;

internal class StockSenseBackgroundService : BackgroundService
{
    private readonly ILogger<StockSenseBackgroundService> _logger;
    private readonly IOptions<SmartStockSenseSettings> _optSmartStockSenseSettings;
    private readonly IServiceScopeFactory _serviceScopeFactory;
    private readonly StockDataProvider _stockDataProvider;
    private readonly IHubContext<NotificationHub> _hubContext;

    public StockSenseBackgroundService(
        ILogger<StockSenseBackgroundService> logger,
        IOptions<SmartStockSenseSettings> optSmartStockSenseSettings,
        IServiceScopeFactory serviceScopeFactory,
        StockDataProvider stockDataProvider,
        IHubContext<NotificationHub> hubContext)
    {
        _logger = logger;
        _optSmartStockSenseSettings = optSmartStockSenseSettings;
        _serviceScopeFactory = serviceScopeFactory;
        _stockDataProvider = stockDataProvider;
        _hubContext = hubContext;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var now = DateTime.Now;
            var currentMinute = now.Minute;
            var today = now.Day;

            if (IsWithinSaturdayOrSunday(now))
            {
                await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
                continue;
            }

            if (IsWithinWorkingHours(now) && ShouldProcessCurrentMinute(currentMinute))
            {
                try
                {
                    await GetStockListAsync(stoppingToken);

                    var todayUserSearchStockList = await GetTodayUserSearchStockListAsync(stoppingToken);
                    if (todayUserSearchStockList == null || todayUserSearchStockList.Count() == 0) return;
                    await UpdateStockInformationAsync(todayUserSearchStockList, stoppingToken);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error retrieving stock list.");
                }
            }

            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
        }
    }

    private bool IsWithinSaturdayOrSunday(DateTime now)
    {
        return now.DayOfWeek == DayOfWeek.Saturday || now.DayOfWeek == DayOfWeek.Sunday;
    }

    private bool IsWithinWorkingHours(DateTime now)
    {
        return (now.TimeOfDay >= _optSmartStockSenseSettings.Value.WorkingHours.MorningStart && now.TimeOfDay <= _optSmartStockSenseSettings.Value.WorkingHours.MorningEnd) ||
               (now.TimeOfDay >= _optSmartStockSenseSettings.Value.WorkingHours.AfternoonStart && now.TimeOfDay <= _optSmartStockSenseSettings.Value.WorkingHours.AfternoonEnd);
    }

    private bool ShouldProcessCurrentMinute(int currentMinute)
    {
        return currentMinute % _optSmartStockSenseSettings.Value.ApiRequestIntervalInMinutes == 0;
    }

    /// <summary>
    /// 1.获取股票列表
    /// </summary>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    private async Task GetStockListAsync(CancellationToken cancellationToken = default)
    {
        using (var scope = _serviceScopeFactory.CreateScope())
        {
            var scopedService = scope.ServiceProvider.GetRequiredService<StockServices>();

            var exist = await scopedService.ExistingAsync();
            if (exist) return;

            var stockList = await _stockDataProvider.GetStockListAsync(null, cancellationToken);
            if (stockList == null || !stockList.Any()) return;

            var stocks = stockList.Select(s => new StockCreateInputModel { StockName = s.StockName, Market = s.Market, StockCode = s.StockCode });
            await scopedService.CreateAsync(stocks);
        }
    }

    /// <summary>
    /// 1.获取今天用户查询的股票列表
    /// </summary>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    private async Task<IEnumerable<Stock>> GetTodayUserSearchStockListAsync(CancellationToken cancellationToken = default)
    {
        using (var scope = _serviceScopeFactory.CreateScope())
        {
            var scopedService = scope.ServiceProvider.GetRequiredService<StockServices>();

            var todayUserSearchStockInformations = await scopedService.GetTodayUserSearchStockListAsync();

            return todayUserSearchStockInformations
                .Select(s => s.Stock)
                .DistinctBy(x => x.StockCode);
        }
    }

    /// <summary>
    /// 2.定时更新股票信息
    /// </summary>
    /// <param name="stocks"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    private async Task UpdateStockInformationAsync(IEnumerable<Stock> stocks, CancellationToken cancellationToken = default)
    {
        foreach (var stock in stocks)
        {
            foreach (StockDataType dataType in Enum.GetValues(typeof(StockDataType)))
            {
                try
                {
                    using (var scope = _serviceScopeFactory.CreateScope())
                    {
                        var scopedService = scope.ServiceProvider.GetRequiredService<StockServices>();
                        await scopedService.UpdateStockInformationAsync(stock.StockCode, dataType);
                        await _hubContext.Clients.All.SendAsync("ReceiveNotification", "数据更新");
                    }
                }
                catch (Exception ex)
                {
                    // 记录错误信息，包括异常消息、堆栈跟踪和股票代码
                    _logger.LogError(ex, "更新股票信息失败。股票代码: {StockCode}, 数据类型: {DataType}", stock.StockCode, dataType);
                    continue; // 继续处理下一个数据类型
                }
            }

        }
    }
}
